package tianrun.ziguan.api.calculate.config.service.common.edit.asset;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.shiro.util.Assert;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import tianrun.ziguan.api.calculate.config.dto.request.common.DeleteBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.InfoBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.AddOrEditBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.SaveBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.FilterField;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.ListBody;
import tianrun.ziguan.api.calculate.config.entity.*;
import tianrun.ziguan.api.calculate.config.listener.RefreshCacheListener;
import tianrun.ziguan.api.calculate.config.service.asset.callabel.ITAssetCalLabelConfigService;
import tianrun.ziguan.api.calculate.config.service.asset.experssion.ITAssetExpressionService;
import tianrun.ziguan.api.calculate.config.service.asset.instance.ITAssetIndicatorsInstanceService;
import tianrun.ziguan.api.calculate.config.service.asset.module.ITAssetIndicatorsModuleService;
import tianrun.ziguan.api.calculate.config.service.business.RefreshCacheService;
import tianrun.ziguan.api.calculate.config.service.common.CommonService;
import tianrun.ziguan.api.calculate.config.service.common.edit.AbstractDealService;
import tianrun.ziguan.api.calculate.config.service.ge.TGeTableConfigService;
import tianrun.ziguan.api.calculate.config.utils.MapUtil;
import tianrun.ziguan.api.common.util.ListUtil;
import tianrun.ziguan.api.common.util.SpringContextUtil;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class AssetIndicatorInstanceDealService extends AbstractDealService {

    private final ITAssetIndicatorsInstanceService indicatorsInstanceService;
    private final ITAssetCalLabelConfigService calLabelConfigService;
    private final ITAssetExpressionService expressionService;
    private final ITAssetIndicatorsModuleService indicatorsModuleService;

    public AssetIndicatorInstanceDealService(TGeTableConfigService tableConfigService, ITAssetIndicatorsInstanceService indicatorsInstanceService, ITAssetCalLabelConfigService calLabelConfigService, ITAssetExpressionService expressionService, ITAssetIndicatorsModuleService indicatorsModuleService) {
        super(tableConfigService);
        this.indicatorsInstanceService = indicatorsInstanceService;

        this.calLabelConfigService = calLabelConfigService;
        this.expressionService = expressionService;
        this.indicatorsModuleService = indicatorsModuleService;
    }

    @Override
    public void before(SaveBody body) {
        String calLabel = MapUtil.getString(body.getData(), "cal_label");
        Long calAssetId = MapUtil.getLong(body.getRawData(), "cal_asset_id");

        List<TAssetIndicatorsModule> moduleByAsset = indicatorsModuleService.getByCalAssetId(Sets.newHashSet(calAssetId));
        if (moduleByAsset.isEmpty()) throw new IllegalArgumentException("未找到组件");
        LambdaQueryWrapper<TAssetIndicatorsInstance> wrapper = Wrappers.<TAssetIndicatorsInstance>lambdaQuery()
                .eq(TAssetIndicatorsInstance::getCalLabel, calLabel)
                .in(TAssetIndicatorsInstance::getModuleId, moduleByAsset.stream().map(TAssetIndicatorsModule::getId).collect(Collectors.toSet()));
        if (Objects.equals(body.getOption(), SaveBody.Option.EDIT)) {
            wrapper.ne(TAssetIndicatorsInstance::getId, body.getId());
        }
        int count = indicatorsInstanceService.count(wrapper);
        Assert.isTrue(count == 0, calLabel + "已存在！");

        //判断是否有自己算自己的情况
        List<JSONObject> configDatas = MapUtil.getList(body.getRawData(), "config_data", JSONObject.class);
        boolean match = configDatas.stream().anyMatch(o -> Objects.equals(o.getString("source"), "self") && Objects.equals(o.getString("label"), calLabel));
        Assert.isTrue(!match, "self中不允许引用要计算的点位："+calLabel);
    }


    @Override
    public String tableName() {
        return "t_asset_indicators_instance";
    }

    @Override
    public void after(SaveBody body) {
        //删除无用的label_config
        List<JSONObject> data = MapUtil.getList(body.getRawData(), "config_data", JSONObject.class);
        Set<Long> ids = data.stream().map(jsonObject -> jsonObject.getLong("id")).filter(Objects::nonNull).collect(Collectors.toSet());
        LambdaQueryWrapper<TAssetCalLabelConfig> wrapper = Wrappers.<TAssetCalLabelConfig>lambdaQuery()
                .eq(TAssetCalLabelConfig::getRelationId, body.getId())
                .eq(TAssetCalLabelConfig::getRelationTable, tableName());
        if (!ids.isEmpty()) {
            wrapper.notIn(TAssetCalLabelConfig::getId, ids);
        }
        calLabelConfigService.remove(wrapper);

        addChildData(body, "config_data", "t_asset_cal_label_config", true);
        addChildData(body, "expression_data", "t_asset_expression", false);

        //redis
        redisFlush(body.getId());
    }

    private void redisFlush(long id) {
        CommonService commonService = SpringContextUtil.getBean(CommonService.class);
        TAssetIndicatorsInstance instance = new JSONObject(commonService.info(InfoBody.of(tableName(), id))).toJavaObject(TAssetIndicatorsInstance.class);
        RefreshCacheListener.flushByModuleId(instance.getModuleId(), "t_asset_indicators_module");
    }

    private Set<Long> addChildData(SaveBody body, String filed, String tableName, boolean isList) {
        CommonService commonService = SpringContextUtil.getBean(CommonService.class);

        if (!isList) {
            JSONObject jsonObject = MapUtil.getObject(body.getRawData(), filed, JSONObject.class);
            Object o = commonService.addOrEdit(getAddOrEditBody(body, tableName, jsonObject));
            return Sets.newHashSet(Long.parseLong(o.toString()));
        }
        List<JSONObject> data = MapUtil.getList(body.getRawData(), filed, JSONObject.class);
        if (ListUtil.isEmpty(data)) return new HashSet<>();
        StringBuilder sb = new StringBuilder();
        Set<Long> ids = data.stream().map(jsonObject -> getAddOrEditBody(body, tableName, jsonObject))
                .map(addOrEditBody -> {
                    try {
                        return Long.parseLong(commonService.addOrEdit(addOrEditBody).toString());
                    } catch (IllegalArgumentException e) {
                        sb.append(e.getMessage()).append(StringPool.NEWLINE);
                        return null;
                    }
                }).filter(Objects::nonNull).collect(Collectors.toSet());
        String message = sb.toString();
        Assert.isTrue(message.isEmpty(), message.trim());
        return ids;
    }

    private AddOrEditBody getAddOrEditBody(SaveBody body, String tableName, JSONObject jsonObject) {
        jsonObject.put("relation_id", body.getId());
        jsonObject.put("relation_table", tableName());
        jsonObject.put("cal_label", MapUtil.getString(body.getData(), "cal_label"));

        AddOrEditBody addOrEditBody = new AddOrEditBody();
        addOrEditBody.setTableName(tableName);
        addOrEditBody.setData(jsonObject);
        return addOrEditBody;
    }

    @Override
    public void delBefore(DeleteBody body) {

    }

    @Override
    public void listAfter(List<Map<String, Object>> result) {
        CommonService commonService = SpringContextUtil.getBean(CommonService.class);

        for (Map<String, Object> bean : result) {
            Object id = bean.get("id");
            ListBody listBody = new ListBody();
            FilterField isValidFilterField = new FilterField("is_valid", FilterField.FilterTypeEnum.equal, "1", FilterField.ConditionalSplicing.and, null);
            FilterField relationIdFilterField = new FilterField("relation_id", FilterField.FilterTypeEnum.equal, id, FilterField.ConditionalSplicing.and, null);
            listBody.setFilterFields(Lists.newArrayList(isValidFilterField, relationIdFilterField));
            listBody.setTableName("t_asset_cal_label_config");
            bean.put("config_data", commonService.list(listBody));

            listBody.setTableName("t_asset_expression");
            bean.put("expression_data", commonService.list(listBody).stream().findFirst().orElse(Maps.newHashMap()));
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public void delAfter(DeleteBody body) {
        body.getIds().forEach(this::redisFlush);
    }

    @Override
    public void infoAfter(Map<String, Object> bean) {

    }


}
